﻿using Com.Lancetinc.Evs.Communications;
using Com.Lancetinc.Evs.Events;
using Com.Lancetinc.Evs.Helpers;
using Com.Lancetinc.Evs.Models;
using Com.Lancetinc.Evs.Services;
using Prism.Commands;
using Prism.Events;
using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Threading;

namespace Com.Lancetinc.Evs.Controls
{
    /// <summary>
    /// 录像中
    /// </summary>
    public partial class InputPageVideoUserControl
    {
        private bool mLedOn = false;
        private int? _ledBrightness;
        private bool _instrumentConnected = false;
        private VideoStateEnum? _videoState;

        private DispatcherTimer mTimer;
        private bool mVideoFileCreated = false;

        #region ExperInfo
        private ExperInfo mExper;

        public static readonly DependencyProperty ExperInfoProperty =
            DependencyProperty.Register(nameof(ExperInfo),
                typeof(ExperInfo),
                typeof(InputPageVideoUserControl), new PropertyMetadata(null, (d, e) =>
                {
                    if (d is not InputPageVideoUserControl self) return;
                    if (e.NewValue is not ExperInfo newExp) return;

                    self.mExper = newExp;
                }));

        public ExperInfo ExperInfo
        {
            get => (ExperInfo)GetValue(ExperInfoProperty);
            set => SetValue(ExperInfoProperty, value);
        }
        #endregion

        #region Commands
        public DelegateCommand StartPauseCommand { get; private set; }
        public DelegateCommand StopCommand { get; private set; }
        public DelegateCommand CaptureScreenCommand { get; private set; }
        public DelegateCommand CapturedScreenCommand { get; private set; }
        public DelegateCommand<string> ChangeBrightnessCommand { get; private set; }
        public DelegateCommand SwitchLedCommand { get; private set; }

        private void CreateCommands()
        {
            StartPauseCommand = new DelegateCommand(ExecuteStartPauseCommand, CanExecuteStartPauseCommand);
            StopCommand = new DelegateCommand(ExecuteStopCommand, CanExecuteStopCommand);
            CaptureScreenCommand = new DelegateCommand(ExecuteCaptureScreenCommand, CanExecuteCaptureScreenCommand);
            CapturedScreenCommand = new DelegateCommand(ExecuteCapturedScreenCommand, CanExecuteCapturedScreenCommand);

            ChangeBrightnessCommand = new DelegateCommand<string>(ExecuteChangeBrightnessCommand, CanExecuteChangeBrightnessCommand);
            SwitchLedCommand = new DelegateCommand(ExecuteSwitchLedCommand, CanExecuteSwitchLedCommand);
        }

        #region SwitchLedCommand
        private bool CanExecuteSwitchLedCommand()
        {
            return _instrumentConnected && _ledBrightness.HasValue;
        }

        private void ExecuteSwitchLedCommand()
        {
            OnRequireSwitchLed();
        }

        #endregion

        public bool IsVideoPlaying => _videoState == VideoStateEnum.Playing;

        private void RefreshButtonState()
        {
            StartPauseCommand.RaiseCanExecuteChanged();
            StopCommand.RaiseCanExecuteChanged();
            CaptureScreenCommand.RaiseCanExecuteChanged();
            ChangeBrightnessCommand.RaiseCanExecuteChanged();
            SwitchLedCommand.RaiseCanExecuteChanged();
            CapturedScreenCommand.RaiseCanExecuteChanged();
        }

        #region StopCommand

        private bool CanExecuteStopCommand()
        {
            return IsVideoPlaying;
        }

        private void ExecuteStopCommand()
        {
            OnRequireStop(RequireStopReason.UserStop);
        }

        #endregion

        #region StartPauseCommand
        private void ExecuteStartPauseCommand()
        {
            mVideoFileCreated = false;

            var isPlaying = ctrlMedia.VideoState == VideoStateEnum.Playing;
            if (!isPlaying)
            {
                OnRequirePlay();
            }
            else
            {
                OnRequirePause();
            }
        }

        private bool CanExecuteStartPauseCommand()
        {
            var ins = ActiveApp.Instance.InstManager.Instrument;
            VideoStateEnum videoState = _videoState ?? VideoStateEnum.Stop;
            return videoState == VideoStateEnum.Stop && ins.InstrumentConnected && ins.EndoscopicConnected;
        }

        #endregion

        #region CaptureScreenCommand
        private void ExecuteCaptureScreenCommand()
        {
            OnRequireCaptureVideoImage();
        }

        private bool CanExecuteCaptureScreenCommand()
        {
            return IsVideoPlaying;
        }

        #endregion

        #region CapturedScreenCommand
        private bool CanExecuteCapturedScreenCommand()
        {
            if (mExper == null) return false;
            return mExper.Images.Count > 0;
        }

        private void ExecuteCapturedScreenCommand()
        {
            var images = mExper.Images;
            if (images.Count == 0)
                return;
            KnownAppEvents.PublishRequireShowCapturedImage(mExper, images.Last(), null);
        }

        #endregion

        #region ChangeBrightnessCommand
        private bool CanExecuteChangeBrightnessCommand(string arg)
        {
            if (string.IsNullOrEmpty(arg))
                return false;

            var isAdd = int.TryParse(arg, out var p) && p > 0;
            if (!mLedOn || !_ledBrightness.HasValue)
                return false;
            // 加
            if (isAdd)
            {
                return _ledBrightness.Value < AppConsts.MaxLedBrightness;
            }
            return _ledBrightness.Value > 1;
        }

        private void ExecuteChangeBrightnessCommand(string arg)
        {
            if (string.IsNullOrEmpty(arg))
                return;

            var isAdd = int.TryParse(arg, out var p) && p > 0;
            ChangeLedBrightness(isAdd);
        }
        #endregion

        #endregion

        public InputPageVideoUserControl()
        {
            InitializeComponent();
            mTimer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(10),
                IsEnabled = false
            };
            mTimer.Tick += MTimer_Tick;

            CreateCommands();

            RegisterEvents();
        }

        private void RegisterEvents()
        {
            KnownAppEvents.RequireCaptureScreenEvent.Subscribe(args =>
            {
                OnRequireCaptureVideoImage();
            }, ThreadOption.UIThread);

            KnownAppEvents.RequireStopVideoRecordEvent.Subscribe(args =>
            {
                OnRequireStop(args.Reason);
            }, ThreadOption.UIThread);

            KnownAppEvents.CaptureImageChangedEvent.Subscribe(args =>
            {
                this.RefreshButtonState();
            }, ThreadOption.UIThread);

            ctrlMedia.VideoStateChanged += CtrlMedia_VideoStateChanged;
        }


        private void MTimer_Tick(object sender, EventArgs e)
        {
            if (!mVideoFileCreated)
            {
                // 通过判断视频文件创建时间，计算视频开始时间
                if (File.Exists(mExper.VideoPath))
                {
                    // 有内容写入的时候
                    if (new FileInfo(mExper.VideoPath).Length > 0)
                    {
                        mTimer.Stop();
                        mVideoFileCreated = true;
                        mExper.Exper.Exper.StartTime = DateTime.Now.AddMilliseconds(-80).FormatForDbUpdate();
                        if (ctrlMedia != null)
                        {
                            ctrlMedia.CtrlVideoTime.ExperBeginTime = mExper.GetStartTime();
                        }

                        try
                        {
                            Data.AppRepositories.Exper.Save(mExper.Exper.Exper);
                        }
                        catch
                        {
                            //
                        }

                        mTimer.Interval = TimeSpan.FromMilliseconds(200);
                        mTimer.Start();
                    }
                }
                return;
            }

            // 视频文件已创建，只需更新录像时间
            var millSeconds = (long)DateTime.Now.Subtract(mExper.GetStartTime()).TotalMilliseconds;
            if (millSeconds > 0)
            {
                if (ctrlMedia != null)
                {
                    ctrlMedia.CtrlVideoTime.VideoMillSeconds = millSeconds;
                }
            }
        }

        private void CtrlMedia_VideoStateChanged(object sender, VideoStateChangeEventArgs e)
        {
            var videoState = ctrlMedia?.VideoState ?? VideoStateEnum.Stop;
            _videoState = videoState;

            var isPlaying = videoState == VideoStateEnum.Playing;
            var isStop = videoState == VideoStateEnum.Stop;
            WpfHelper.ExecLater(RefreshButtonState, 100);

            if (isStop)
            {
                mTimer.Stop();
                KnownAppEvents.PublishVideoRecordStopped(mExper);
            }

            if (isPlaying)
            {
                ctrlMedia.CtrlVideoTime.Action = VideoTimeDisplayAction.Recoding;
                mTimer.Interval = TimeSpan.FromMilliseconds(10);
                mTimer.Start();
            }
        }

        private async void OnRequirePlay()
        {
            var instManager = ActiveApp.Instance.InstManager;

            // 开始录像或继续录像
            var videoCtrl = ctrlMedia;
            if (videoCtrl == null) return;

            var isPlaying = videoCtrl.VideoState == VideoStateEnum.Playing;
            if (!isPlaying)
            {
                // 开始录像的场合
                var result = await ExperRunController.PrepareRun(instManager, mExper, 10);
                if (result == null)
                {
                    return;
                }

                var width = result.VideoWidth;
                var height = result.VideoHeight;

                videoCtrl.SetCaptureParams(result.VideoInputName, ref width, ref height, result.VideoFormat, mExper.VideoPath);

                result.VideoWidth = width;
                result.VideoHeight = height;
            }

            if (!await videoCtrl.Play())
                return;

            KnownAppEvents.PublishVideoRecordStarted(mExper);
        }

        private void OnRequirePause()
        {
            ctrlMedia?.Pause();
            KnownAppEvents.PublishVideoRecordPaused(mExper);
        }

        public async void OnRequireStop(RequireStopReason reason)
        {
            if (!IsVideoPlaying) return;

            if (reason == RequireStopReason.UserStop)
            {
                // 确定要停止录像吗？
                var r = await MessageHelper.ShowOkCancelByMsgIdAsync("ConfirmStopRecording");
                if (r == MessageBoxResult.Cancel)
                {
                    return;
                }
            }

            ctrlMedia.Stop(reason);
        }

        private void OnRequireCaptureVideoImage()
        {
            try
            {
                if (!IsVideoPlaying) return;

                var imageName = ExperManager.Default.CreateImageName();
                var path = mExper.GetImageFullPath(imageName);
                if (ctrlMedia != null)
                {
                    ctrlMedia.CaptureImage(path);
                }

                if (!mExper.Images.Contains(imageName))
                {
                    mExper.Images.Add(imageName);
                }

                KnownAppEvents.PublishCaptureImageChanged(mExper, 0);
                this.RefreshButtonState();
            }
            catch (Exception ex)
            {
                AppStatusManager.Default.AppStatus.ErrorMessage = ex.Message;
            }
        }

        private void OnRequireSwitchLed()
        {
            var instManager = ActiveApp.Instance.InstManager;
            var ledBrightness = !mLedOn ? AppConsts.DefaultLedBrightness : (byte)0;
            instManager.SendChangeLedBrightness(ledBrightness);
        }

        private void ChangeLedBrightness(bool isPlus)
        {
            var instManager = ActiveApp.Instance.InstManager;
            var ledBrightness = instManager.Instrument.LedBrightness ?? AppConsts.DefaultLedBrightness;
            if (isPlus)
            {
                if (ledBrightness < AppConsts.MaxLedBrightness)
                {
                    ledBrightness++;
                }
            }
            else
            {
                if (ledBrightness > 1)
                {
                    ledBrightness--;
                }
            }

            instManager.Send(PacketHelper.CreateLed(ledBrightness));
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            RefreshInstrumentState();
            ActiveApp.Instance.InstManager.StateChanged += InstManager_StateChanged;
        }

        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            ActiveApp.Instance.InstManager.StateChanged -= InstManager_StateChanged;
        }

        private void InstManager_StateChanged(object sender, InstrumentInfoChangedEventArgs e)
        {
            RefreshInstrumentState(e.Instrument);
        }

        private void RefreshInstrumentState(IInstrumentState ins = null)
        {
            ins ??= ActiveApp.Instance.InstManager.Instrument;
            if (ins == null) return;

            _instrumentConnected = ins.InstrumentConnected;
            mLedOn = _instrumentConnected && ins.LedBrightness > 0;
            _ledBrightness = !_instrumentConnected ? null : ins.LedBrightness;

            WpfHelper.ExecSafe(RefreshButtonState);
        }
    }
}
